EB3I n1 2025 scRNAseq
-
LESSON BLOCK (I)
-
A nice subtitle





1 Start Rstudio



2 Warm-up

  • We now set common parameters as new variables, once and for all for this session :
# setparam


## Set your project name
# WARNING : Do not just copy-paste this ! It's MY project name ! Put YOURS !!
project_name <- "ebaii_sc_teachers"


## Control if the project_name exists on the cluster
cat('PATH CHECK : ', dir.exists(paste0('/shared/projects/', project_name)))
Show output
PATH CHECK :  TRUE
## Seed for the RNG
my_seed <- 1337L

# ## Empty droplets max p-value
# max_p <- 1E-03


3 Prepare the data structure [PREPROC.2]

3.1 Main directory

# maindir

## Preparing the path
TD_dir <- paste0("/shared/projects/", project_name, "/SC_TD")

## Creating the root directory
# dir.create(path = TD_dir, recursive = TRUE)

## Print the root directory on-screen
print(TD_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD"

3.2 Current session

# sessiondir

## Creating the session (Preproc.2) directory
session_dir <- paste0(TD_dir, "/02_Preproc.2")
# dir.create(path = session_dir, recursive = TRUE)

## Print the session directory on-screen
print(session_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/02_Preproc.2"

3.3 Input directory

# indir

## Creating the INPUT data directory
input_dir <- paste0(session_dir, "/DATA")
# dir.create(path = input_dir, recursive = TRUE)

## Print the input directory on-screen
print(input_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/02_Preproc.2/DATA"

3.4 Genelists directory

This is a directory where we will store additional information from knowledge bases about genes used to estimate the cell cycle phase of cells.

# resdir

res_dir <- paste0(TD_dir, "/Resources")
glist_dir <- paste0(res_dir, "/Genelists")

## Create the directory
# dir.create(path = glist_dir, recursive = TRUE)

## Print the resources directory on-screen
print(glist_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/Resources/Genelists"

3.5 Output directory

# outdir

## Creating the OUTPUT data directory
output_dir <- paste0(session_dir, "/RESULTS")
# dir.create(path = output_dir, recursive = TRUE)

## Print the output directory on-screen
print(output_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/02_Preproc.2/RESULTS"


4 Load the raw matrix [PreProc.2]

We retrieve the input data file

# mat_dl

local <- FALSE

## The raw count matrix we will start from
scmat_source <- "GSM4861194_gex_2_raw_gene_expression.tsv.gz"

## Download the file from Zenodo
if (!local) {
  
  ### ZenID
  zen_id <- "14033941"
  ### Zen Path
  zen_backup_file <- paste0("https://zenodo.org/records/",
                            zen_id,
                            "/files/",
                            scmat_source)
  
  ## The path to the locally saved input file
  scmat_file <- paste0(input_dir,
                       '/',
                       scmat_source)
  ## Download the file
  download.file(url = zen_backup_file,
                destfile = scmat_file)
} else {
  ebaii_session <- '2538_eb3i_n1_2025'
  scmat_file <- paste0(
      '/shared/projects/',
      ebaii_session,
      '/atelier_scrnaseq/TD/BACKUP/TSV/',
      scmat_source)
}

We can load it into R :

# mat_load

scmat <- as.matrix(
  utils::read.table(
    file = scmat_file, 
    header = TRUE, 
    sep = "\t"))

## Displaying its size in-memory (this is a basic matrix)
format(utils::object.size(scmat), units = "auto")
Show output
[1] "545.7 Mb"


5 Load the genelists resources [PreProc.2]

We retrieve the genelists

# gl_dl

local <- FALSE

## The genelist files
mito_source <- "mus_musculus_mito_symbols_20191015.rds"
ribo_source <- "mus_musculus_cribo_symbols_20191015.rds"
stress_source <- "mus_musculus_stress_symbols_20200224.rds"
gl_sources <- c(mito_source, ribo_source, stress_source)

## The (future) local files
mito_file <- paste0(input_dir, '/', mito_source)
ribo_file <- paste0(input_dir, '/', ribo_source)
stress_file <- paste0(input_dir, '/', stress_source)
gl_files <- c(mito_file, ribo_file, stress_file)

## Download the file from Zenodo
if (!local) {
  
  ### ZenID
  zen_id <- "14037355"
  ### Looping on files
  for (glf in seq_along(gl_sources)) {
    ### Zen Path
    zen_backup_file <- paste0("https://zenodo.org/records/",
                              zen_id,
                              "/files/",
                              gl_sources[glf])
    
    ## Download the file
    download.file(url = zen_backup_file,
                  destfile = gl_files[glf])
  }
  rm(gl_files)
} else {  ## Local mode
  ebaii_session <- '2538_eb3i_n1_2025'
  localbackup_dir <- paste0('/shared/projects/', ebaii_session, '/atelier_scrnaseq/TD/RESOURCES/GENELISTS/')
  mito_file <- paste0(localbackup_dir, '/mus_musculus_mito_symbols_20191015.rds')
  ribo_source <- paste0(input_dir, '/mus_musculus_cribo_symbols_20191015.rds')
  stress_source <- paste0(input_dir, '/mus_musculus_stress_symbols_20200224.rds')
}



Description:

This file describes the different steps to the quality control based on:

  • the number of UMI (transcripts) detected per cell
  • the number of genes detected per cell
  • the proportion of transcripts related to the genes encoded in the mitochondria, per cell
  • the proportion of transcripts related to the genes encoding ribosomal units, per cell
  • the proportion of transcripts related to stress signature, per cell

Input data: Seurat object containing all cells (filtered count matrix)

Output data: Seurat object annotated for the cells to filter out for downstream analysis, called sobj_TD3A_qc_annotated.rds

Next steps: Cells annotation for cell type, doublet status and cell cycle status

6 Context

In this file, we used a dataset from the Paiva et al. publication.

The study concerns thymus autonomy:

  • The thymus is an “organ of passage”, critical in its function to the adaptive immune system for the maturation of T cell lymphocytes.

  • This maturation involves two main steps, performed thanks to macrophages:

    • Positive selection : keeping cells that successfully develop react appropriately with MHC immune receptors of the body
    • Negative selection : keeping cells that do not react against natural proteins of the body.
  • Thymus autonomy is a natural mechanism that allows to create T cells in the thymus by differentiation and cell competition, even when normal progenitors from the bone marrow are lacking, in critical conditions.

  • This mechanism is known in its effects, but the cells involved in are not.

  • This study is of importance in the health field, as this mechanism relies on a temporary loss of control of the cell normal functions.

  • The consequence is that if thymus is in autonomy for too long (few weeks), this is a prelude for leukemia !

  • Organism is : mus musculus

  • Individuals are : mice in development, grafted

  • The design corresponds to two conditions (Test / control)

    • Control : thymus from wild type newborn mouse transplanted into wild type juvenile mouse. In this control case, donor T-cells progenitors (DN3) were replaced by host cells 3 weeks after transplantation.
    • Test : thymus from wild type newborn mouse transplanted in KO Rag-/- type juvenile mouse (the KO partially impairs their ability to produce T-cell progenitors in normal amounts). In this test case, donor T-cells progenitors (DN3) were replaced by host cells 9 weeks after transplantation, showing that the donor DN3 cells outlived their normal lifespan by ~6 weeks.

You will mainly work on the KO sample (‘TD3A’). The input data consists in a count matrix, as a gzipped tabular text file, that contains everything needed to create a basic Seurat object : * The expressions counts * The feature names (here, gene symbols) * The barcode names

This matrix has already been filtered for empty droplets.

7 Environment

We load the package of interest:

library(dplyr)
library(ggplot2)
library(Seurat)

.libPaths()
Show output
[1] "/shared/ifbstor1/home/smella/R/x86_64-conda-linux-gnu-library/4.2"
[2] "/shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/R/library"   

8 Data

We load the count matrix:

# # Set input data
# input_data = "/shared/projects/2538_eb3i_n1_2025/atelier_scrnaseq/TD/BACKUP/TSV/GSM4861194_gex_2_raw_gene_expression.tsv.gz"
# 
# # Load data
# mat = read.table(input_data,
#                  header = TRUE,
#                  sep = "\t")
# mat = as.matrix(mat)
# mat = Matrix::Matrix(mat,
#                      sparse = TRUE)

dim(scmat)
Show output
[1] 31053  4587
scmat[c(1:5), c(1:5)]
Show output
        AAACCTGAGACGCTTT.1 AAACCTGAGGCATTGG.1 AAACCTGGTCAACATC.1
Xkr4                     0                  0                  0
Gm1992                   0                  0                  0
Gm37381                  0                  0                  0
Rp1                      0                  0                  0
Sox17                    0                  0                  0
        AAACCTGTCGAGGTAG.1 AAACCTGTCGATCCCT.1
Xkr4                     0                  0
Gm1992                   0                  0
Gm37381                  0                  0
Rp1                      0                  0
Sox17                    0                  0

We build a Seurat object from the count matrix:

sobj = Seurat::CreateSeuratObject(counts = scmat,
                                  assay = "RNA",
                                  project = "TD3A")
sobj
Show output
An object of class Seurat 
31053 features across 4587 samples within 1 assay 
Active assay: RNA (31053 features, 0 variable features)
 1 layer present: counts

We do not need the count matrix:

rm(scmat)

We set the filtering thresholds based on quality control-related metrics. Adjust them as necessary based on the figures.

cut_nCount_RNA = 300
cut_log10_nCount_RNA <- 3
cut_nFeature_RNA = 750
cut_percent_mt = 5
cut_percent_rb = 30
cut_percent_st = 6

We define a nice palette to visualize the QC metrics:

color_palette = c("lightgray", "#FDBB84", "#EF6548", "#7F0000", "black")

For the QC metrics related to the proportion of UMI belongs to a specific gene sets, we need to load the gene sets.

mito_symbols = readRDS(mito_file)
mito_symbols
Show output
 [1] "mt-Atp6" "mt-Atp8" "mt-Co1"  "mt-Co2"  "mt-Co3"  "mt-Cytb" "mt-Nd1" 
 [8] "mt-Nd2"  "mt-Nd3"  "mt-Nd4"  "mt-Nd4l" "mt-Nd5"  "mt-Nd6"  "mt-Rnr1"
[15] "mt-Rnr2" "mt-Ta"   "mt-Tc"   "mt-Td"   "mt-Te"   "mt-Tf"   "mt-Tg"  
[22] "mt-Th"   "mt-Ti"   "mt-Tk"   "mt-Tl1"  "mt-Tl2"  "mt-Tm"   "mt-Tn"  
[29] "mt-Tp"   "mt-Tq"   "mt-Tr"   "mt-Ts1"  "mt-Ts2"  "mt-Tt"   "mt-Tv"  
[36] "mt-Tw"   "mt-Ty"  
ribo_symbols = readRDS(ribo_file)
ribo_symbols
Show output
 [1] "Rpsa"   "Rps2"   "Rps3"   "Rps3a"  "Rps4x"  "Rps5"   "Rps6"   "Rps7"  
 [9] "Rps8"   "Rps9"   "Rps10"  "Rps11"  "Rps12"  "Rps13"  "Rps14"  "Rps15" 
[17] "Rps15a" "Rps16"  "Rps17"  "Rps18"  "Rps19"  "Rps20"  "Rps21"  "Rps23" 
[25] "Rps24"  "Rps25"  "Rps26"  "Rps27"  "Rps27a" "Rps28"  "Rps29"  "Rps30" 
[33] "Rpl3"   "Rpl4"   "Rpl5"   "Rpl6"   "Rpl7"   "Rpl7a"  "Rpl8"   "Rpl9"  
[41] "Rpl10"  "Rpl10a" "Rpl11"  "Rpl12"  "Rpl13"  "Rpl13a" "Rpl14"  "Rpl15" 
[49] "Rpl17"  "Rpl18"  "Rpl18a" "Rpl19"  "Rpl21"  "Rpl22"  "Rpl23"  "Rpl23a"
[57] "Rpl24"  "Rpl26"  "Rpl27"  "Rpl27a" "Rpl28"  "Rpl29"  "Rpl30"  "Rpl31" 
[65] "Rpl32"  "Rpl34"  "Rpl35"  "Rpl35a" "Rpl36"  "Rpl44"  "Rpl37"  "Rpl37a"
[73] "Rpl38"  "Rpl39"  "Rpl40"  "Rpl41"  "Rplp0"  "Rplp1"  "Rplp2" 
stress_symbols = readRDS(stress_file)
stress_symbols
Show output
 [1] "Atf3"     "Cmss1"    "Diaph1"   "Egr1"     "Fos"      "Gls"     
 [7] "Gm48099"  "lsp90aa1" "lsp90ab1" "lfrd1"    "Jak1"     "Junb"    
[13] "Kpna1"    "Nup210l"  "Peak1"    "Stat3"    "Actb"     "Camk1d"  
[19] "Chka"     "Clic4"    "Fodl2"    "Hspa8"    "Jun"      "Jund"    
[25] "Klf6"     "Litaf"    "Pecam1"   "Ptma"     "Sik3"     "Spag9"   
[31] "Arih1"    "Azin1"    "Brd2"     "Chd4"     "Ctnnb1"   "Dennd4a" 
[37] "Elf1"     "G3bp1"    "Ints6"    "Kdm6b"    "Lsmem1"   "Man1a"   
[43] "Med13"    "Nfkbia"   "Nfkbiz"   "Nop58"    "Piezo1"   "Ppp1r15a"
[49] "Prkcg"    "Sqstm1"   "Taf4b"    "Tmsb4x"   "Ubc"      "Zfp36"   
[55] "Abtb2"    "Adamts1"  "Adamts9"  "Ahnak"    "Ankrd28"  "Atp2a2"  
[61] "Baz1a"    "Btg2"     "Ccdc138"  "Cd44"     "Cdkn1a"   "Elf2"    
[67] "Ep400"    "Epas1"    "Erf"      "Ern1"     "Fabp4"    "Fosb"    
[73] "Gm10073"  "Gsk3a"    "H3f3a"    "H3f3b"    "Hivep2"   "Klf4"    
[79] "Lmna"     "lapkapk2" "Mapre1"   "Msn"      "Mylip"    "Nabp1"   
[85] "Ncl"      "Nsd3"     "Nufip2"   "Plekhg2"  "Ppp1cb"   "Rnf19b"  
[91] "Rps20"    "Rtn4"     "Runx1"    "Sertad2"  "Sptan1"   "Top1"    
[97] "Vcl"      "Zbtb11"  

We keep only the gene symbols available in our data:

mito_symbols = intersect(mito_symbols, rownames(sobj))
mito_symbols
Show output
 [1] "mt-Atp6" "mt-Atp8" "mt-Co1"  "mt-Co2"  "mt-Co3"  "mt-Cytb" "mt-Nd1" 
 [8] "mt-Nd2"  "mt-Nd3"  "mt-Nd4"  "mt-Nd4l" "mt-Nd5"  "mt-Nd6" 
ribo_symbols = intersect(ribo_symbols, rownames(sobj))
ribo_symbols
Show output
 [1] "Rpsa"   "Rps2"   "Rps3"   "Rps4x"  "Rps5"   "Rps6"   "Rps7"   "Rps8"  
 [9] "Rps9"   "Rps10"  "Rps11"  "Rps12"  "Rps13"  "Rps14"  "Rps15"  "Rps15a"
[17] "Rps16"  "Rps17"  "Rps18"  "Rps19"  "Rps20"  "Rps21"  "Rps23"  "Rps24" 
[25] "Rps25"  "Rps26"  "Rps27"  "Rps27a" "Rps28"  "Rps29"  "Rpl3"   "Rpl4"  
[33] "Rpl5"   "Rpl6"   "Rpl7"   "Rpl7a"  "Rpl8"   "Rpl9"   "Rpl10"  "Rpl10a"
[41] "Rpl11"  "Rpl12"  "Rpl13"  "Rpl13a" "Rpl14"  "Rpl15"  "Rpl17"  "Rpl18" 
[49] "Rpl18a" "Rpl19"  "Rpl21"  "Rpl22"  "Rpl23"  "Rpl23a" "Rpl24"  "Rpl26" 
[57] "Rpl27"  "Rpl27a" "Rpl28"  "Rpl29"  "Rpl30"  "Rpl31"  "Rpl32"  "Rpl34" 
[65] "Rpl35"  "Rpl35a" "Rpl36"  "Rpl37"  "Rpl37a" "Rpl38"  "Rpl39"  "Rpl41" 
[73] "Rplp0"  "Rplp1"  "Rplp2" 
stress_symbols = intersect(stress_symbols, rownames(sobj))
stress_symbols
Show output
 [1] "Atf3"     "Cmss1"    "Diaph1"   "Egr1"     "Fos"      "Gls"     
 [7] "Gm48099"  "Jak1"     "Junb"     "Kpna1"    "Nup210l"  "Peak1"   
[13] "Stat3"    "Actb"     "Camk1d"   "Chka"     "Clic4"    "Hspa8"   
[19] "Jun"      "Jund"     "Klf6"     "Litaf"    "Pecam1"   "Ptma"    
[25] "Sik3"     "Spag9"    "Arih1"    "Azin1"    "Brd2"     "Chd4"    
[31] "Ctnnb1"   "Dennd4a"  "Elf1"     "G3bp1"    "Ints6"    "Kdm6b"   
[37] "Lsmem1"   "Man1a"    "Med13"    "Nfkbia"   "Nfkbiz"   "Nop58"   
[43] "Piezo1"   "Ppp1r15a" "Prkcg"    "Sqstm1"   "Taf4b"    "Tmsb4x"  
[49] "Ubc"      "Zfp36"    "Abtb2"    "Adamts1"  "Adamts9"  "Ahnak"   
[55] "Ankrd28"  "Atp2a2"   "Baz1a"    "Btg2"     "Ccdc138"  "Cd44"    
[61] "Cdkn1a"   "Elf2"     "Ep400"    "Epas1"    "Erf"      "Ern1"    
[67] "Fabp4"    "Fosb"     "Gm10073"  "Gsk3a"    "H3f3a"    "H3f3b"   
[73] "Hivep2"   "Klf4"     "Lmna"     "Mapre1"   "Msn"      "Mylip"   
[79] "Nabp1"    "Ncl"      "Nsd3"     "Nufip2"   "Plekhg2"  "Ppp1cb"  
[85] "Rnf19b"   "Rps20"    "Rtn4"     "Runx1"    "Sertad2"  "Sptan1"  
[91] "Top1"     "Vcl"      "Zbtb11"  

Note: A more robust analysis may use the gene identifiers instead of gene symbols.

9 Fast-processing

To visualize the low quality cells, we generate a projection. Understanding the commands below is the purpose of next course sessions. So just run:

sobj = Seurat::NormalizeData(sobj)
sobj = Seurat::ScaleData(sobj)
sobj = Seurat::FindVariableFeatures(sobj)
sobj = Seurat::RunPCA(sobj)
sobj = Seurat::RunUMAP(sobj, dims = c(1:20))

sobj
Show output
An object of class Seurat 
31053 features across 4587 samples within 1 assay 
Active assay: RNA (31053 features, 2000 variable features)
 3 layers present: counts, data, scale.data
 2 dimensional reductions calculated: pca, umap

We can now visualize the cells on a 2D projection:

Seurat::DimPlot(sobj,
                reduction = "umap",
                cols = "black")
Show plot

10 Quality control

10.1 Compute metrics

What is already available in the Seurat object ?

head(sobj@meta.data)
Show output
                   orig.ident nCount_RNA nFeature_RNA
AAACCTGAGACGCTTT.1       TD3A       2813         1594
AAACCTGAGGCATTGG.1       TD3A       2072         1365
AAACCTGGTCAACATC.1       TD3A       2025         1341
AAACCTGTCGAGGTAG.1       TD3A       1877         1241
AAACCTGTCGATCCCT.1       TD3A       2216         1441
AAACGGGCACTTACGA.1       TD3A       2445         1428

How do the two first QC metrics vary ?

summary(sobj@meta.data)
Show output
 orig.ident    nCount_RNA     nFeature_RNA 
 TD3A:4587   Min.   :  502   Min.   :  22  
             1st Qu.: 1986   1st Qu.:1291  
             Median : 2397   Median :1476  
             Mean   : 3573   Mean   :1638  
             3rd Qu.: 3134   3rd Qu.:1733  
             Max.   :48875   Max.   :5975  

In the column nCount_RNA, the maximum is far from the third quartile. For visualization purpose, we transform this column to log10 scale.

sobj$log10_nCount_RNA = log10(sobj$nCount_RNA)

summary(sobj@meta.data)
Show output
 orig.ident    nCount_RNA     nFeature_RNA  log10_nCount_RNA
 TD3A:4587   Min.   :  502   Min.   :  22   Min.   :2.701   
             1st Qu.: 1986   1st Qu.:1291   1st Qu.:3.298   
             Median : 2397   Median :1476   Median :3.380   
             Mean   : 3573   Mean   :1638   Mean   :3.429   
             3rd Qu.: 3134   3rd Qu.:1733   3rd Qu.:3.496   
             Max.   :48875   Max.   :5975   Max.   :4.689   

We compute the percentage of UMI related for each of the three list of genes. First, we compute the proportion of transcripts related to the genes encoded in the mitochondria, per cell.

sobj = Seurat::PercentageFeatureSet(
  sobj,
  assay = "RNA",
  features = mito_symbols,
  col.name = "percent_mt")

# # Alternative way to do (almost) the same:
# sobj = Seurat::PercentageFeatureSet(
#   sobj,
#   assay = "RNA",
#   pattern = "^mt-",
#   col.name = "percent_mt")

summary(sobj$percent_mt)
Show output
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   1.985   2.489   2.997   3.125  97.250 

Then, we compute the proportion of transcripts related to the genes encoding ribosomal units, per cell:

sobj = Seurat::PercentageFeatureSet(
  sobj,
  assay = "RNA",
  features = ribo_symbols,
  col.name = "percent_rb")

# # Alternative way to do (almost) the same:
# sobj = Seurat::PercentageFeatureSet(
#   sobj,
#   assay = "RNA",
#   pattern = "^Rp[l|s][a-z]?[0-9]*[a,x]?$",
#   col.name = "percent_rb")

summary(sobj$percent_rb)
Show output
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   7.884   9.577  10.880  12.167  42.010 

Finally, we compute the proportion of transcripts related to stress signature, per cell:

sobj = Seurat::PercentageFeatureSet(
  sobj,
  assay = "RNA",
  features = stress_symbols,
  col.name = "percent_st")

summary(sobj$percent_st)
Show output
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   2.946   3.338   3.368   3.749  10.198 

We know have all the QC-related metrics:

summary(sobj@meta.data)
Show output
 orig.ident    nCount_RNA     nFeature_RNA  log10_nCount_RNA   percent_mt    
 TD3A:4587   Min.   :  502   Min.   :  22   Min.   :2.701    Min.   : 0.000  
             1st Qu.: 1986   1st Qu.:1291   1st Qu.:3.298    1st Qu.: 1.985  
             Median : 2397   Median :1476   Median :3.380    Median : 2.489  
             Mean   : 3573   Mean   :1638   Mean   :3.429    Mean   : 2.997  
             3rd Qu.: 3134   3rd Qu.:1733   3rd Qu.:3.496    3rd Qu.: 3.125  
             Max.   :48875   Max.   :5975   Max.   :4.689    Max.   :97.250  
   percent_rb       percent_st    
 Min.   : 0.000   Min.   : 0.000  
 1st Qu.: 7.884   1st Qu.: 2.946  
 Median : 9.577   Median : 3.338  
 Mean   :10.880   Mean   : 3.368  
 3rd Qu.:12.167   3rd Qu.: 3.749  
 Max.   :42.010   Max.   :10.198  

10.2 Failing cells

We identify the cells that do not pass the quality control. This will be used for the visualization and for filtering. If the filtering thresholds are modified, do not forget to run again this chunk.

fail_percent_mt = sobj@meta.data %>%
  dplyr::filter(percent_mt > cut_percent_mt) %>%
  rownames()

fail_percent_rb = sobj@meta.data %>%
  dplyr::filter(percent_rb > cut_percent_rb) %>%
  rownames()

fail_percent_st = sobj@meta.data %>%
  dplyr::filter(percent_st > cut_percent_st) %>%
  rownames()

fail_nCount_RNA = sobj@meta.data %>%
  dplyr::filter(nCount_RNA < cut_nCount_RNA) %>%
  rownames()

fail_nFeature_RNA = sobj@meta.data %>%
  dplyr::filter(nFeature_RNA < cut_nFeature_RNA) %>%
  rownames()

10.3 Visualization

This is difficult to handle the distribution of these metrics across cells. We opt for various visualization ways:

  • histogram, showing the distribution of the metric
  • violin/box plot, showing the distribution of the metric, useful if several datasets are considered
  • UMAP (or alternatively, tSNE), showing the distribution of the metric over a 2D projection of cells

You may choose one of these visualization ways.

10.3.1 Number of UMI

10.3.1.1 Define the code

We write a lot of code to create our figures of interest:

# Histogram showing:
# - the distribution of the metric
# - an estimated density
# - the filtering threshold as a straight line
p_hist = ggplot(sobj@meta.data, aes(x = log10_nCount_RNA)) +
  geom_histogram(aes(y = after_stat(density)),
                          colour = "black", fill = "#F8766D", bins = 100) +
  geom_density(alpha = 0, col = "blue", lwd = 0.75) +
  geom_vline(xintercept = cut_log10_nCount_RNA, col = "red") +
  labs(title = paste0("Threshold for log10_nCount_RNA is: ", cut_log10_nCount_RNA)) +
  theme_classic() +
  theme(plot.title = element_text(hjust = 0.5))

# Violin plot:
# - is easily accessible in the Seurat package
# - can or not display the cells (set `pt.size = 0` to hide the cells)
# - is useful when several datasets have been merged
p_violin = Seurat::VlnPlot(sobj, features = "log10_nCount_RNA") +
  geom_hline(yintercept = cut_log10_nCount_RNA, col = "red") +
  theme(axis.title.x = element_blank(),
                 legend.position = "none")

# # Box plot is similar to violin plot but not in the Seurat package
# p_boxplot = ggplot(sobj@meta.data, aes(y = log10_nCount_RNA,
#                                                 x = orig.ident)) +
#   geom_boxplot(colour = "black", fill = "#F8766D") +
#   geom_jitter(width = 0.3, size = 0.001) +
#   geom_hline(yintercept = cut_log10_nCount_RNA, col = "red") +
#   theme_classic() +
#   theme(axis.title.x = element_blank())

# This 2D representation shows the distribution of the metric over cells
p_umap = FeaturePlot(sobj,
                     reduction = "umap",
                     features = "log10_nCount_RNA") +
  scale_color_gradientn(colors = color_palette) +
  theme(aspect.ratio = 1)

# This 2D representation shows the low quality cells in color
# `order = "fail"` is used to display failing cells on front
# before, we need to define the "failorpass" column in sobj@meta.data
# It corresponds to "fail" for cells that do no pass the threshold,
# or "pass" otherwise
sobj$failorpass = ifelse(colnames(sobj) %in% fail_nCount_RNA,
                         yes = "fail", no = "pass") %>%
  as.factor()

p_fail = DimPlot(sobj,
                 group.by = "failorpass",
                 order = "fail") +
  scale_color_manual(values = c("#F8766D", "gray80"),
                     breaks = levels(sobj$failorpass)) +
  labs(title = "log10_nCount_RNA",
       subtitle = paste0(length(fail_nCount_RNA), " cells fail (",
                         round(100*length(fail_nCount_RNA)/ncol(sobj), 2), " %)")) +
  theme(aspect.ratio = 1,
        plot.title = element_text(hjust = 0.5),
        plot.subtitle = element_text(hjust = 0.5))

sobj$failorpass = NULL # remove the column (it was temporary)

# We use the patchwork package to arrange all figures together
patchwork::wrap_plots(p_umap, p_fail, p_hist, p_violin) +
  patchwork::plot_layout(nrow = 1, widths = c(1, 1, 2, 1))
Show plot

10.3.1.2 Define a function

Instead of copying-pasting this section of code for all QC-metrics, we design a function using the template below:

my_function_name = function(param1, param2) {
  # do something with the parameter values
  output = "something"
  
  return(output)
}

Here is the function:

print_1_qc_metric = function(object = sobj,
                             qc = "log10_nCount_RNA",
                             cut_qc = cut_log10_nCount_RNA,
                             failing_cells = fail_nCount_RNA) {
  # Description of the parameters:
  # - sobj : the Seurat object, with default value to the one
  # - qc : CHARACTER : the QC metric, must be a column in sobj@meta.data
  # - cut_qc : NUMERIC : the filtering threshold for the QC metric
  # - failing_cells : CHARACTER VECTOR : the cells that fail the QC
  
  # Histogram
  p_hist = ggplot(object@meta.data, aes(x = .data[[qc]])) +
    geom_histogram(aes(y = after_stat(density)),
                            colour = "black", fill = "#F8766D", bins = 100) +
    geom_density(alpha = 0, col = "blue", lwd = 0.75) +
    geom_vline(xintercept = cut_qc, col = "red") +
    labs(title = paste0("Threshold for ", qc, " is: ", cut_qc)) +
    theme_classic() +
    theme(plot.title = element_text(hjust = 0.5))
  
  # Violin plot
  p_violin = Seurat::VlnPlot(object, features = qc) +
    geom_hline(yintercept = cut_qc, col = "red") +
    theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  # Box plot
  p_boxplot = ggplot(object@meta.data, aes(y = .data[[qc]],
                                                    x = "orig.ident")) +
    geom_boxplot(colour = "black", fill = "#F8766D") +
    geom_jitter(width = 0.3, size = 0.001) +
    geom_hline(yintercept = cut_qc, col = "red") +
    theme_classic() +
    theme(axis.title.x = element_blank())
  
  # Feature plot
  p_umap = Seurat::FeaturePlot(object,
                               reduction = "umap",
                               features = qc) +
    scale_color_gradientn(colors = color_palette) +
    theme(aspect.ratio = 1)
  
  # Dim plot
  object$failorpass = ifelse(colnames(object) %in% failing_cells,
                             yes = "fail", no = "pass") %>%
    as.factor()
  
  p_fail = Seurat::DimPlot(object,
                           group.by = "failorpass",
                           order = "fail") +
    scale_color_manual(values = c("#F8766D", "gray80"),
                                breaks = levels(object$failorpass)) +
    labs(title = qc,
                  subtitle = paste0(length(failing_cells), " cells fail (",
                                    round(100*length(failing_cells)/ncol(sobj), 2), " %)")) +
    theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5),
                   plot.subtitle = element_text(hjust = 0.5))
  
  # Patchwork
  p = patchwork::wrap_plots(p_umap, p_fail, p_hist, p_violin, p_boxplot) +
    patchwork::plot_layout(nrow = 1, widths = c(1, 1, 2, 1, 1))
  
  return(p)
}

10.3.1.3 Check the function

This is working for log10_nCount_RNA, because it is used to define default parameter values:

print_1_qc_metric()
Show plot

but also works by specifying the parameter values:

print_1_qc_metric(sobj,
                  qc = "log10_nCount_RNA",
                  cut_qc = cut_log10_nCount_RNA,
                  failing_cells = fail_nCount_RNA)
Show plot

So we can copy-paste only this chunk for the next QC metrics !

10.3.2 Number of genes

print_1_qc_metric(sobj,
                  qc = "nFeature_RNA",
                  cut_qc = cut_nFeature_RNA,
                  failing_cells = fail_nFeature_RNA)
Show plot

10.3.3 Mitochondrial genes expression

print_1_qc_metric(sobj,
                  qc = "percent_mt",
                  cut_qc = cut_percent_mt,
                  failing_cells = fail_percent_mt)
Show plot

10.3.4 Ribosomal genes expression

print_1_qc_metric(sobj,
                  qc = "percent_rb",
                  cut_qc = cut_percent_rb,
                  failing_cells = fail_percent_rb)
Show plot

11 Filtering

We could filter out cells based on these 5 QC metrics now. It is also possible to wait, perform various annotations such as cell type annotation or cell cycle phase scoring, to better characterize the low quality cells.

To filter a Seurat object, we use the subset function:

sobj_filtered = subset(sobj, invert = TRUE,
                       cells = unique(c(fail_nCount_RNA, fail_nFeature_RNA,
                                        fail_percent_mt, fail_percent_rb, fail_percent_st)))
sobj_filtered
Show output
An object of class Seurat 
31053 features across 4218 samples within 1 assay 
Active assay: RNA (31053 features, 2000 variable features)
 3 layers present: counts, data, scale.data
 2 dimensional reductions calculated: pca, umap

We are going to save the object annotated for cells that fail the quality control, regardless the metrics. So we add a single column to sobj@meta.data :

sobj$fail_qc = ifelse(test = colnames(sobj) %in% colnames(sobj_filtered),
                      yes = "pass",
                      no = "fail")

table(sobj$fail_qc)
Show output

fail pass 
 369 4218 

12 Save

We save the non-filtered Seurat object:

saveRDS(sobj, file = paste0(output_dir, "/sobj_TD3A_qc_annotated.rds"))

This Seurat object can then be the input object for annotation, definition of a new projection and downstream analyses.

13 R session

This is a good practice to show the version of the packages used in this notebook.

sessionInfo()
Show output
R version 4.4.1 (2024-06-14)
Platform: x86_64-conda-linux-gnu
Running under: Ubuntu 22.04.5 LTS

Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/libopenblasp-r0.3.29.so;  LAPACK version 3.12.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Paris
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] Seurat_5.3.0       SeuratObject_5.1.0 sp_2.2-0           ggplot2_3.5.2     
[5] dplyr_1.1.4       

loaded via a namespace (and not attached):
  [1] deldir_2.0-4           pbapply_1.7-2          gridExtra_2.3         
  [4] rlang_1.1.6            magrittr_2.0.3         RcppAnnoy_0.0.22      
  [7] spatstat.geom_3.4-1    matrixStats_1.5.0      ggridges_0.5.6        
 [10] compiler_4.4.1         png_0.1-8              vctrs_0.6.5           
 [13] reshape2_1.4.4         stringr_1.5.1          pkgconfig_2.0.3       
 [16] fastmap_1.2.0          labeling_0.4.3         rmdformats_1.0.4      
 [19] promises_1.3.2         rmarkdown_2.29         ggbeeswarm_0.7.2      
 [22] purrr_1.0.4            xfun_0.52              cachem_1.1.0          
 [25] jsonlite_2.0.0         goftest_1.2-3          later_1.4.2           
 [28] spatstat.utils_3.1-4   irlba_2.3.5.1          parallel_4.4.1        
 [31] cluster_2.1.6          R6_2.6.1               ica_1.0-3             
 [34] spatstat.data_3.1-6    bslib_0.9.0            stringi_1.8.7         
 [37] RColorBrewer_1.1-3     reticulate_1.42.0      spatstat.univar_3.1-3 
 [40] parallelly_1.45.0      lmtest_0.9-40          jquerylib_0.1.4       
 [43] scattermore_1.2        Rcpp_1.0.14            bookdown_0.39         
 [46] knitr_1.50             tensor_1.5             future.apply_1.11.3   
 [49] zoo_1.8-14             sctransform_0.4.2      httpuv_1.6.15         
 [52] Matrix_1.7-3           splines_4.4.1          igraph_2.1.4          
 [55] tidyselect_1.2.1       abind_1.4-8            rstudioapi_0.17.1     
 [58] dichromat_2.0-0.1      yaml_2.3.10            spatstat.random_3.4-1 
 [61] codetools_0.2-20       miniUI_0.1.2           spatstat.explore_3.4-3
 [64] listenv_0.9.1          lattice_0.22-6         tibble_3.2.1          
 [67] plyr_1.8.9             shiny_1.10.0           withr_3.0.2           
 [70] ROCR_1.0-11            ggrastr_1.0.2          evaluate_1.0.3        
 [73] Rtsne_0.17             future_1.49.0          fastDummies_1.7.5     
 [76] survival_3.7-0         polyclip_1.10-7        fitdistrplus_1.2-2    
 [79] pillar_1.10.2          KernSmooth_2.23-24     plotly_4.10.4         
 [82] generics_0.1.4         RcppHNSW_0.6.0         scales_1.4.0          
 [85] globals_0.18.0         xtable_1.8-4           glue_1.8.0            
 [88] lazyeval_0.2.2         tools_4.4.1            data.table_1.17.4     
 [91] RSpectra_0.16-2        RANN_2.6.2             dotCall64_1.2         
 [94] cowplot_1.1.3          grid_4.4.1             tidyr_1.3.1           
 [97] nlme_3.1-165           patchwork_1.3.0        beeswarm_0.4.0        
[100] vipor_0.4.7            cli_3.6.5              spatstat.sparse_3.1-0 
[103] spam_2.11-1            viridisLite_0.4.2      uwot_0.2.3            
[106] gtable_0.3.6           sass_0.4.10            digest_0.6.37         
[109] progressr_0.15.1       ggrepel_0.9.6          htmlwidgets_1.6.4     
[112] farver_2.1.2           htmltools_0.5.8.1      lifecycle_1.0.4       
[115] httr_1.4.7             mime_0.13              MASS_7.3-65           
LS0tCnRpdGxlOiAiPENFTlRFUj5FQjNJIG4xIDIwMjUgc2NSTkFzZXE8QlI+LTxCUj4gPEI+TEVTU09OIEJMT0NLIChJKTwvQj48QlI+LTxCUj5BIG5pY2Ugc3VidGl0bGU8L0NFTlRFUj4iCmRhdGU6ICIyMDI1LTE2LTIxLjIyIgphdXRob3I6CiAgLSBuYW1lOiAiRUJBSUkgbjEgc2NSTkFzZXEgVGVhbSIKICAtIG5hbWU6ICJGaXJzdCBBVVRIT1IiCiAgICBlbWFpbDogImZpcnN0LmF1dGhvckBzY3JuYXNlcS5jb20iCiAgLSBuYW1lOiAiU2Vjb25kIEFVVEhPUiIKICAgIGVtYWlsOiAic2Vjb25kLmF1dGhvckBzY3JuYXNlcS5jb20iCm91dHB1dDoKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIGZpZ193aWR0aDogOAogICAgZmlnX2hlaWdodDogNgogICAgaGlnaGxpZ2h0OiB0YW5nbyAgIyMgVGhlbWUgZm9yIHRoZSBjb2RlIGNodW5rcwogICAgZW1iZWRfZm9udHM6IFRSVUUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyMgQWRkcyBudW1iZXIgdG8gaGVhZGVycyAoc2VjdGlvbnMpCiAgICB0aGVtZTogZmxhdGx5ICAjIyBDU1MgdGhlbWUgZm9yIHRoZSBIVE1MIHBhZ2UKICAgIGNvbGxhcHNlZDogdHJ1ZSAgIyMgQnkgZGVmYXVsdCwgdGhlIFRPQyBpcyBmb2xkZWQKICAgIHRvY19kZXB0aDogMwogICAgc21vb3RoX3Njcm9sbDogdHJ1ZSAjIyBTbW9vdGggc2Nyb2xsIG9mIHRoZSBIVE1MIHBhZ2UKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlICMjIEluY2x1ZGVzIGFsbCBwbG90cy9pbWFnZXMgd2l0aGluIHRoZSBIVE1MCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgdGhlIFJtZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aHVtYm5haWxzOiBmYWxzZQogICAgbGlnaHRib3g6IHRydWUKICAgIGZpZ19jYXB0aW9uOiBmYWxzZQogICAgZ2FsbGVyeTogdHJ1ZQogICAgdXNlX2Jvb2tkb3duOiB0cnVlCmFsd2F5c19hbGxvd19odG1sOiB0cnVlICMjIEFsbG93IHBsYWluIEhUTUwgY29kZSBpbiB0aGUgUm1kCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCjwhLS0ga25pdCBzZXR1cCAtLT4KCmBgYHtyIGtuaXRfc2V0dXAsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLCAgICAgICAgIyBQcmludCB0aGUgY29kZQogIGV2YWwgPSBUUlVFLCAgICAgICAgIyBSdW4gY29tbWFuZCBsaW5lcwogIG1lc3NhZ2UgPSBGQUxTRSwgICAgIyBQcmludCBtZXNzYWdlcwogIHByb21wdCA9IEZBTFNFLCAgICAgIyBEbyBub3QgZGlzcGxheSBwcm9tcHQKICBjb21tZW50ID0gTkEsICAgICAgICMgTm8gY29tbWVudHMgb24gdGhpcyBzZWN0aW9uCiAgd2FybmluZyA9IEZBTFNFLCAgICAjIERpc3BsYXkgd2FybmluZ3MKICB0aWR5ID0gRkFMU0UsCiAgZmlnLmFsaWduPSJjZW50ZXIiLCAKICAjIHJlc3VsdHMgPSAnaGlkZScsCiAgd2lkdGggPSAxMDAgICAgICAgIyBOdW1iZXIgb2YgY2hhcmFjdGVycyBwZXIgbGluZQopCmBgYAoKCjwhLS0gQ1NTIHRvIGNvbG9yIGNodW5rcyBhbmQgb3V0cHV0cyAtLT4KCgpgYGB7Y3NzLCBlY2hvPUZBTFNFfQoubm90cnVuIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBibGFjayAhaW1wb3J0YW50Owp9Ci5ub3RydW5vIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBhcXVhbWFyaW5lICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGxpbWVncmVlbiAhaW1wb3J0YW50Owp9Ci5xdWVzdGlvbm8gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLmFuc3dlciB7CiAgYmFja2dyb3VuZC1jb2xvcjogbmF2YWpvd2hpdGUgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBicm93biAhaW1wb3J0YW50Owp9Ci5hbnN3ZXJvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYmV5b25kIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBwdXJwbGUgIWltcG9ydGFudDsKfQouYmV5b25kbyB7CiAgYmFja2dyb3VuZC1jb2xvcjogdmlvbGV0ICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9CmBgYAoKCjwhLS0gSG9vayB0byBoYW5kbGUgY29kZSBibG9ja3Mgb3V0cHV0IGZvbGRpbmcgLS0+CgpgYGB7ciBrbml0X2hvb2ssIGVjaG8gPSBGQUxTRX0KaG9va3MgPSBrbml0cjo6a25pdF9ob29rcyRnZXQoKQpob29rX2ZvbGRhYmxlID0gZnVuY3Rpb24odHlwZSkgewogIGZvcmNlKHR5cGUpCiAgZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgcmVzID0gaG9va3NbW3R5cGVdXSh4LCBvcHRpb25zKQogICAgCiAgICBpZiAoaXNGQUxTRShvcHRpb25zW1twYXN0ZTAoImZvbGQuIiwgdHlwZSldXSkpIHJldHVybihyZXMpCiAgICAKICAgIHBhc3RlMCgKICAgICAgIjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgIiwgdHlwZSwgIjwvc3VtbWFyeT5cblxuIiwKICAgICAgcmVzLAogICAgICAiXG5cbjwvZGV0YWlscz4iCiAgICApCiAgfQp9CmtuaXRyOjprbml0X2hvb2tzJHNldCgKICBvdXRwdXQgPSBob29rX2ZvbGRhYmxlKCJvdXRwdXQiKSwKICBwbG90ID0gaG9va19mb2xkYWJsZSgicGxvdCIpCikKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxjZW50ZXI+IVtdKGViM2lfYmFubmVyLnBuZyk8L2NlbnRlcj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBTdGFydCBSc3R1ZGlvCgotICAgVXNpbmcgdGhlIFtPcGVuT25EZW1hbmQgY2hlYXQKICAgIHNoZWV0XShodHRwczovL2lmYi1lbGl4aXJmci5naXRodWIuaW8vRUJBSUkvMjAyMy9lYmFpaW4xL1NpbmdsZUNlbGwvMjAyNF9URF9PcGVuT25EZW1hbmQuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAqKltMSU5LX1RPX1VQREFURV0qKiwKICAgIGNvbm5lY3QgdG8gdGhlIFtPcGVuT25EZW1hbmQKICAgIHBvcnRhbF0oaHR0cHM6Ly9vbmRlbWFuZC5jbHVzdGVyLmZyYW5jZS1iaW9pbmZvcm1hdGlxdWUuZnIpe3RhcmdldD0iX2JsYW5rIn0gYW5kCiAgICAqKmNyZWF0ZSBhIFJzdHVkaW8gc2Vzc2lvbioqIHdpdGggdGhlIHJpZ2h0IHJlc291cmNlIHJlcXVpcmVtZW50cywgdGhhbmtzIHRvIHRoZSBjaGVhdCBzaGVldC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFdhcm0tdXAKCi0gICBXZSBub3cgc2V0ICoqY29tbW9uIHBhcmFtZXRlcnMqKiBhcyBuZXcgdmFyaWFibGVzLCBvbmNlIGFuZCBmb3IgYWxsIGZvciB0aGlzCnNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW19CiMgc2V0cGFyYW0KCgojIyBTZXQgeW91ciBwcm9qZWN0IG5hbWUKIyBXQVJOSU5HIDogRG8gbm90IGp1c3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0IG5hbWUgISBQdXQgWU9VUlMgISEKcHJvamVjdF9uYW1lIDwtICJlYmFpaV9zY190ZWFjaGVycyIKCgojIyBDb250cm9sIGlmIHRoZSBwcm9qZWN0X25hbWUgZXhpc3RzIG9uIHRoZSBjbHVzdGVyCmNhdCgnUEFUSCBDSEVDSyA6ICcsIGRpci5leGlzdHMocGFzdGUwKCcvc2hhcmVkL3Byb2plY3RzLycsIHByb2plY3RfbmFtZSkpKQoKIyMgU2VlZCBmb3IgdGhlIFJORwpteV9zZWVkIDwtIDEzMzdMCgojICMjIEVtcHR5IGRyb3BsZXRzIG1heCBwLXZhbHVlCiMgbWF4X3AgPC0gMUUtMDMKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUHJlcGFyZSB0aGUgZGF0YSBzdHJ1Y3R1cmUgW1BSRVBST0MuMl0KCiMjIE1haW4gZGlyZWN0b3J5CgpgYGB7ciBtYWluZGlyfQojIG1haW5kaXIKCiMjIFByZXBhcmluZyB0aGUgcGF0aApURF9kaXIgPC0gcGFzdGUwKCIvc2hhcmVkL3Byb2plY3RzLyIsIHByb2plY3RfbmFtZSwgIi9TQ19URCIpCgojIyBDcmVhdGluZyB0aGUgcm9vdCBkaXJlY3RvcnkKIyBkaXIuY3JlYXRlKHBhdGggPSBURF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgcm9vdCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KFREX2RpcikKCmBgYAoKIyMgQ3VycmVudCBzZXNzaW9uCgpgYGB7ciBzZXNzaW9uZGlyfQojIHNlc3Npb25kaXIKCiMjIENyZWF0aW5nIHRoZSBzZXNzaW9uIChQcmVwcm9jLjIpIGRpcmVjdG9yeQpzZXNzaW9uX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiLzAyX1ByZXByb2MuMiIpCiMgZGlyLmNyZWF0ZShwYXRoID0gc2Vzc2lvbl9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgc2Vzc2lvbiBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KHNlc3Npb25fZGlyKQoKYGBgCgojIyBJbnB1dCBkaXJlY3RvcnkKCmBgYHtyIGluZGlyfQojIGluZGlyCgojIyBDcmVhdGluZyB0aGUgSU5QVVQgZGF0YSBkaXJlY3RvcnkKaW5wdXRfZGlyIDwtIHBhc3RlMChzZXNzaW9uX2RpciwgIi9EQVRBIikKIyBkaXIuY3JlYXRlKHBhdGggPSBpbnB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgaW5wdXQgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChpbnB1dF9kaXIpCgpgYGAKCiMjIEdlbmVsaXN0cyBkaXJlY3RvcnkKClRoaXMgaXMgYSBkaXJlY3Rvcnkgd2hlcmUgd2Ugd2lsbCBzdG9yZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGZyb20gCmtub3dsZWRnZSBiYXNlcyBhYm91dCBnZW5lcyB1c2VkIHRvIGVzdGltYXRlIHRoZSBjZWxsIGN5Y2xlIHBoYXNlIG9mIGNlbGxzLgoKYGBge3IgcmVzZGlyfQojIHJlc2RpcgoKcmVzX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiL1Jlc291cmNlcyIpCmdsaXN0X2RpciA8LSBwYXN0ZTAocmVzX2RpciwgIi9HZW5lbGlzdHMiKQoKIyMgQ3JlYXRlIHRoZSBkaXJlY3RvcnkKIyBkaXIuY3JlYXRlKHBhdGggPSBnbGlzdF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgcmVzb3VyY2VzIGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoZ2xpc3RfZGlyKQoKYGBgCgojIyBPdXRwdXQgZGlyZWN0b3J5CgpgYGB7ciBvdXRkaXJ9CiMgb3V0ZGlyCgojIyBDcmVhdGluZyB0aGUgT1VUUFVUIGRhdGEgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL1JFU1VMVFMiKQojIGRpci5jcmVhdGUocGF0aCA9IG91dHB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgb3V0cHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQob3V0cHV0X2RpcikKCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIExvYWQgdGhlIHJhdyBtYXRyaXggW1ByZVByb2MuMl0KCldlIHJldHJpZXZlIHRoZSBpbnB1dCBkYXRhIGZpbGUKCmBgYHtyIG1hdF9kbH0KIyBtYXRfZGwKCmxvY2FsIDwtIEZBTFNFCgojIyBUaGUgcmF3IGNvdW50IG1hdHJpeCB3ZSB3aWxsIHN0YXJ0IGZyb20Kc2NtYXRfc291cmNlIDwtICJHU000ODYxMTk0X2dleF8yX3Jhd19nZW5lX2V4cHJlc3Npb24udHN2Lmd6IgoKIyMgRG93bmxvYWQgdGhlIGZpbGUgZnJvbSBaZW5vZG8KaWYgKCFsb2NhbCkgewogIAogICMjIyBaZW5JRAogIHplbl9pZCA8LSAiMTQwMzM5NDEiCiAgIyMjIFplbiBQYXRoCiAgemVuX2JhY2t1cF9maWxlIDwtIHBhc3RlMCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHplbl9pZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIvZmlsZXMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjbWF0X3NvdXJjZSkKICAKICAjIyBUaGUgcGF0aCB0byB0aGUgbG9jYWxseSBzYXZlZCBpbnB1dCBmaWxlCiAgc2NtYXRfZmlsZSA8LSBwYXN0ZTAoaW5wdXRfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICcvJywKICAgICAgICAgICAgICAgICAgICAgICBzY21hdF9zb3VyY2UpCiAgIyMgRG93bmxvYWQgdGhlIGZpbGUKICBkb3dubG9hZC5maWxlKHVybCA9IHplbl9iYWNrdXBfZmlsZSwKICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc2NtYXRfZmlsZSkKfSBlbHNlIHsKICBlYmFpaV9zZXNzaW9uIDwtICcyNTM4X2ViM2lfbjFfMjAyNScKICBzY21hdF9maWxlIDwtIHBhc3RlMCgKICAgICAgJy9zaGFyZWQvcHJvamVjdHMvJywKICAgICAgZWJhaWlfc2Vzc2lvbiwKICAgICAgJy9hdGVsaWVyX3Njcm5hc2VxL1REL0JBQ0tVUC9UU1YvJywKICAgICAgc2NtYXRfc291cmNlKQp9CmBgYAoKV2UgY2FuIGxvYWQgaXQgaW50byBSIDoKCmBgYHtyIG1hdF9sb2FkfQojIG1hdF9sb2FkCgpzY21hdCA8LSBhcy5tYXRyaXgoCiAgdXRpbHM6OnJlYWQudGFibGUoCiAgICBmaWxlID0gc2NtYXRfZmlsZSwgCiAgICBoZWFkZXIgPSBUUlVFLCAKICAgIHNlcCA9ICJcdCIpKQoKIyMgRGlzcGxheWluZyBpdHMgc2l6ZSBpbi1tZW1vcnkgKHRoaXMgaXMgYSBiYXNpYyBtYXRyaXgpCmZvcm1hdCh1dGlsczo6b2JqZWN0LnNpemUoc2NtYXQpLCB1bml0cyA9ICJhdXRvIikKCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIExvYWQgdGhlIGdlbmVsaXN0cyByZXNvdXJjZXMgW1ByZVByb2MuMl0KCldlIHJldHJpZXZlIHRoZSBnZW5lbGlzdHMKCmBgYHtyIGdsX2RsfQojIGdsX2RsCgpsb2NhbCA8LSBGQUxTRQoKIyMgVGhlIGdlbmVsaXN0IGZpbGVzCm1pdG9fc291cmNlIDwtICJtdXNfbXVzY3VsdXNfbWl0b19zeW1ib2xzXzIwMTkxMDE1LnJkcyIKcmlib19zb3VyY2UgPC0gIm11c19tdXNjdWx1c19jcmlib19zeW1ib2xzXzIwMTkxMDE1LnJkcyIKc3RyZXNzX3NvdXJjZSA8LSAibXVzX211c2N1bHVzX3N0cmVzc19zeW1ib2xzXzIwMjAwMjI0LnJkcyIKZ2xfc291cmNlcyA8LSBjKG1pdG9fc291cmNlLCByaWJvX3NvdXJjZSwgc3RyZXNzX3NvdXJjZSkKCiMjIFRoZSAoZnV0dXJlKSBsb2NhbCBmaWxlcwptaXRvX2ZpbGUgPC0gcGFzdGUwKGlucHV0X2RpciwgJy8nLCBtaXRvX3NvdXJjZSkKcmlib19maWxlIDwtIHBhc3RlMChpbnB1dF9kaXIsICcvJywgcmlib19zb3VyY2UpCnN0cmVzc19maWxlIDwtIHBhc3RlMChpbnB1dF9kaXIsICcvJywgc3RyZXNzX3NvdXJjZSkKZ2xfZmlsZXMgPC0gYyhtaXRvX2ZpbGUsIHJpYm9fZmlsZSwgc3RyZXNzX2ZpbGUpCgojIyBEb3dubG9hZCB0aGUgZmlsZSBmcm9tIFplbm9kbwppZiAoIWxvY2FsKSB7CiAgCiAgIyMjIFplbklECiAgemVuX2lkIDwtICIxNDAzNzM1NSIKICAjIyMgTG9vcGluZyBvbiBmaWxlcwogIGZvciAoZ2xmIGluIHNlcV9hbG9uZyhnbF9zb3VyY2VzKSkgewogICAgIyMjIFplbiBQYXRoCiAgICB6ZW5fYmFja3VwX2ZpbGUgPC0gcGFzdGUwKCJodHRwczovL3plbm9kby5vcmcvcmVjb3Jkcy8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ZW5faWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIvZmlsZXMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2xfc291cmNlc1tnbGZdKQogICAgCiAgICAjIyBEb3dubG9hZCB0aGUgZmlsZQogICAgZG93bmxvYWQuZmlsZSh1cmwgPSB6ZW5fYmFja3VwX2ZpbGUsCiAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gZ2xfZmlsZXNbZ2xmXSkKICB9CiAgcm0oZ2xfZmlsZXMpCn0gZWxzZSB7ICAjIyBMb2NhbCBtb2RlCiAgZWJhaWlfc2Vzc2lvbiA8LSAnMjUzOF9lYjNpX24xXzIwMjUnCiAgbG9jYWxiYWNrdXBfZGlyIDwtIHBhc3RlMCgnL3NoYXJlZC9wcm9qZWN0cy8nLCBlYmFpaV9zZXNzaW9uLCAnL2F0ZWxpZXJfc2NybmFzZXEvVEQvUkVTT1VSQ0VTL0dFTkVMSVNUUy8nKQogIG1pdG9fZmlsZSA8LSBwYXN0ZTAobG9jYWxiYWNrdXBfZGlyLCAnL211c19tdXNjdWx1c19taXRvX3N5bWJvbHNfMjAxOTEwMTUucmRzJykKICByaWJvX3NvdXJjZSA8LSBwYXN0ZTAoaW5wdXRfZGlyLCAnL211c19tdXNjdWx1c19jcmlib19zeW1ib2xzXzIwMTkxMDE1LnJkcycpCiAgc3RyZXNzX3NvdXJjZSA8LSBwYXN0ZTAoaW5wdXRfZGlyLCAnL211c19tdXNjdWx1c19zdHJlc3Nfc3ltYm9sc18yMDIwMDIyNC5yZHMnKQp9CmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgoKCgoKCgoKLS0tCgoqKkRlc2NyaXB0aW9uKio6CgpUaGlzIGZpbGUgZGVzY3JpYmVzIHRoZSBkaWZmZXJlbnQgc3RlcHMgdG8gdGhlIHF1YWxpdHkgY29udHJvbCBiYXNlZCBvbjoKCiogdGhlIG51bWJlciBvZiBVTUkgKHRyYW5zY3JpcHRzKSBkZXRlY3RlZCBwZXIgY2VsbAoqIHRoZSBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgcGVyIGNlbGwKKiB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIHRoZSBnZW5lcyBlbmNvZGVkIGluIHRoZSBtaXRvY2hvbmRyaWEsIHBlciBjZWxsCiogdGhlIHByb3BvcnRpb24gb2YgdHJhbnNjcmlwdHMgcmVsYXRlZCB0byB0aGUgZ2VuZXMgZW5jb2Rpbmcgcmlib3NvbWFsIHVuaXRzLCBwZXIgY2VsbAoqIHRoZSBwcm9wb3J0aW9uIG9mIHRyYW5zY3JpcHRzIHJlbGF0ZWQgdG8gc3RyZXNzIHNpZ25hdHVyZSwgcGVyIGNlbGwKCioqSW5wdXQgZGF0YSoqOiBTZXVyYXQgb2JqZWN0IGNvbnRhaW5pbmcgYWxsIGNlbGxzIChmaWx0ZXJlZCBjb3VudCBtYXRyaXgpCgoqKk91dHB1dCBkYXRhKio6IFNldXJhdCBvYmplY3QgYW5ub3RhdGVkIGZvciB0aGUgY2VsbHMgdG8gZmlsdGVyIG91dCBmb3IgZG93bnN0cmVhbSBhbmFseXNpcywgY2FsbGVkIGBzb2JqX1REM0FfcWNfYW5ub3RhdGVkLnJkc2AKCioqTmV4dCBzdGVwcyoqOiBDZWxscyBhbm5vdGF0aW9uIGZvciBjZWxsIHR5cGUsIGRvdWJsZXQgc3RhdHVzIGFuZCBjZWxsIGN5Y2xlIHN0YXR1cwoKIyBDb250ZXh0CgpJbiB0aGlzIGZpbGUsIHdlIHVzZWQgYSBkYXRhc2V0IGZyb20gdGhlIFtQYWl2YSBldCBhbC5dKGh0dHBzOi8vZmVicy5vbmxpbmVsaWJyYXJ5LndpbGV5LmNvbS9kb2kvZnVsbC8xMC4xMTExL2ZlYnMuMTQ2NTEpIHB1YmxpY2F0aW9uLgoKVGhlIHN0dWR5IGNvbmNlcm5zICoqdGh5bXVzIGF1dG9ub215Kio6CgoqIFRoZSB0aHltdXMgaXMgYW4gIm9yZ2FuIG9mIHBhc3NhZ2UiLCBjcml0aWNhbCBpbiBpdHMgZnVuY3Rpb24gdG8gdGhlIGFkYXB0aXZlIGltbXVuZSBzeXN0ZW0gZm9yIHRoZSBtYXR1cmF0aW9uIG9mIFQgY2VsbCBseW1waG9jeXRlcy4KKiBUaGlzIG1hdHVyYXRpb24gaW52b2x2ZXMgdHdvIG1haW4gc3RlcHMsIHBlcmZvcm1lZCB0aGFua3MgdG8gbWFjcm9waGFnZXM6CiAgKiBQb3NpdGl2ZSBzZWxlY3Rpb24gOiBrZWVwaW5nIGNlbGxzIHRoYXQgc3VjY2Vzc2Z1bGx5IGRldmVsb3AgcmVhY3QgYXBwcm9wcmlhdGVseSB3aXRoIE1IQyBpbW11bmUgcmVjZXB0b3JzIG9mIHRoZSBib2R5CiAgKiBOZWdhdGl2ZSBzZWxlY3Rpb24gOiBrZWVwaW5nIGNlbGxzIHRoYXQgZG8gbm90IHJlYWN0IGFnYWluc3QgbmF0dXJhbCBwcm90ZWlucyBvZiB0aGUgYm9keS4KKiBUaHltdXMgX2F1dG9ub215XyBpcyBhIG5hdHVyYWwgbWVjaGFuaXNtIHRoYXQgYWxsb3dzIHRvIGNyZWF0ZSBUIGNlbGxzIGluIHRoZSB0aHltdXMgYnkgZGlmZmVyZW50aWF0aW9uIGFuZCBjZWxsIGNvbXBldGl0aW9uLCBldmVuIHdoZW4gbm9ybWFsIHByb2dlbml0b3JzIGZyb20gdGhlIGJvbmUgbWFycm93IGFyZSBsYWNraW5nLCBpbiBjcml0aWNhbCBjb25kaXRpb25zLgoqIFRoaXMgbWVjaGFuaXNtIGlzIGtub3duIGluIGl0cyBlZmZlY3RzLCBidXQgdGhlIGNlbGxzIGludm9sdmVkIGluIGFyZSBub3QuCiogVGhpcyBzdHVkeSBpcyBvZiBpbXBvcnRhbmNlIGluIHRoZSBoZWFsdGggZmllbGQsIGFzIHRoaXMgbWVjaGFuaXNtIHJlbGllcyBvbiBhIHRlbXBvcmFyeSBsb3NzIG9mIGNvbnRyb2wgb2YgdGhlIGNlbGwgbm9ybWFsIGZ1bmN0aW9ucy4KKiBUaGUgY29uc2VxdWVuY2UgaXMgdGhhdCBpZiB0aHltdXMgaXMgaW4gYXV0b25vbXkgZm9yIHRvbyBsb25nIChmZXcgd2Vla3MpLCB0aGlzIGlzIGEgcHJlbHVkZSBmb3IgbGV1a2VtaWEgIQoKICA8Y2VudGVyPiFbXSh0aHltdXNfYXV0b25vbXkucG5nKTwvY2VudGVyPgoKKiBPcmdhbmlzbSBpcyA6ICoqbXVzIG11c2N1bHVzKioKKiBJbmRpdmlkdWFscyBhcmUgOiBtaWNlICoqaW4gZGV2ZWxvcG1lbnQsIGdyYWZ0ZWQqKgoqIFRoZSBkZXNpZ24gY29ycmVzcG9uZHMgdG8gKip0d28gY29uZGl0aW9ucyoqIChUZXN0IC8gY29udHJvbCkKICAqIENvbnRyb2wgOiB0aHltdXMgZnJvbSAqKndpbGQgdHlwZSoqIG5ld2Jvcm4gbW91c2UgdHJhbnNwbGFudGVkIGludG8gKip3aWxkIHR5cGUqKiBqdXZlbmlsZSBtb3VzZS4gSW4gdGhpcyBjb250cm9sIGNhc2UsICoqZG9ub3IqKiBULWNlbGxzIHByb2dlbml0b3JzIChETjMpIHdlcmUgcmVwbGFjZWQgYnkgKipob3N0KiogY2VsbHMgKiozIHdlZWtzKiogYWZ0ZXIgdHJhbnNwbGFudGF0aW9uLgogICogVGVzdCA6IHRoeW11cyBmcm9tICoqd2lsZCB0eXBlKiogbmV3Ym9ybiBtb3VzZSB0cmFuc3BsYW50ZWQgaW4gKipLTyBSYWctLy0qKiB0eXBlIGp1dmVuaWxlIG1vdXNlICh0aGUgS08gcGFydGlhbGx5IGltcGFpcnMgdGhlaXIgYWJpbGl0eSB0byBwcm9kdWNlIFQtY2VsbCBwcm9nZW5pdG9ycyBpbiBub3JtYWwgYW1vdW50cykuIEluIHRoaXMgdGVzdCBjYXNlLCAqKmRvbm9yKiogVC1jZWxscyBwcm9nZW5pdG9ycyAoRE4zKSB3ZXJlIHJlcGxhY2VkIGJ5ICoqaG9zdCoqIGNlbGxzICoqOSB3ZWVrcyoqIGFmdGVyIHRyYW5zcGxhbnRhdGlvbiwgc2hvd2luZyB0aGF0IHRoZSBkb25vciBETjMgY2VsbHMgb3V0bGl2ZWQgdGhlaXIgbm9ybWFsIGxpZmVzcGFuIGJ5IH42IHdlZWtzLgogIAogIDxjZW50ZXI+IVtdKHBhaXZhX3d0X2tvLnBuZyk8L2NlbnRlcj4KICAKCllvdSB3aWxsIG1haW5seSB3b3JrIG9uIHRoZSAqKktPIHNhbXBsZSAoJ1REM0EnKSoqLiBUaGUgaW5wdXQgZGF0YSBjb25zaXN0cyBpbiBhICoqY291bnQgbWF0cml4KiosIGFzIGEgZ3ppcHBlZCB0YWJ1bGFyIHRleHQgZmlsZSwgdGhhdCBjb250YWlucyBldmVyeXRoaW5nIG5lZWRlZCB0byBjcmVhdGUgYSBiYXNpYyBTZXVyYXQgb2JqZWN0IDoKICAqIFRoZSBleHByZXNzaW9ucyAqKmNvdW50cyoqCiAgKiBUaGUgKipmZWF0dXJlIG5hbWVzKiogKGhlcmUsIGdlbmUgc3ltYm9scykKICAqIFRoZSAqKmJhcmNvZGUgbmFtZXMqKgoKVGhpcyBtYXRyaXggaGFzIGFscmVhZHkgYmVlbiAqKmZpbHRlcmVkIGZvciBlbXB0eSBkcm9wbGV0cyoqLgoKPCEtLSAjIFByZS1yZXF1aXNpdGVzIC0tPgoKPCEtLSBUbyBvYnRhaW4gdGhpcyBmaWxlIGFuZCBzdGFydHMgdGhlIFRELCBjb3B5LXBhc3RlIHRoZSBmb2xkZXIgaW4geW91ciBwcm9qZWN0LCBlaXRoZXIgdXNpbmcgdGhlIGdyYXBoaWNhbCBpbnRlcmZhY2UsIG9yIGluIHRoZSBUZXJtaW5hbDogLS0+CgoKCjwhLS0gVGhlIHRyZWUgc2hvdWxkIGxvb2tzIGxpa2U6IC0tPgoKPCEtLSBgYGB7YmFzaH0gLS0+CjwhLS0gbHMgLS0+CjwhLS0gYGBgIC0tPgoKIyBFbnZpcm9ubWVudAoKV2UgbG9hZCB0aGUgcGFja2FnZSBvZiBpbnRlcmVzdDoKCmBgYHtyIHBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoU2V1cmF0KQoKLmxpYlBhdGhzKCkKYGBgCgojIERhdGEKCldlIGxvYWQgdGhlIGNvdW50IG1hdHJpeDoKCmBgYHtyIGxvYWRfY291bnRfbWF0cml4fQojICMgU2V0IGlucHV0IGRhdGEKIyBpbnB1dF9kYXRhID0gIi9zaGFyZWQvcHJvamVjdHMvMjUzOF9lYjNpX24xXzIwMjUvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvVFNWL0dTTTQ4NjExOTRfZ2V4XzJfcmF3X2dlbmVfZXhwcmVzc2lvbi50c3YuZ3oiCiMgCiMgIyBMb2FkIGRhdGEKIyBtYXQgPSByZWFkLnRhYmxlKGlucHV0X2RhdGEsCiMgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLAojICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IikKIyBtYXQgPSBhcy5tYXRyaXgobWF0KQojIG1hdCA9IE1hdHJpeDo6TWF0cml4KG1hdCwKIyAgICAgICAgICAgICAgICAgICAgICBzcGFyc2UgPSBUUlVFKQoKZGltKHNjbWF0KQpzY21hdFtjKDE6NSksIGMoMTo1KV0KYGBgCgpXZSBidWlsZCBhIFNldXJhdCBvYmplY3QgZnJvbSB0aGUgY291bnQgbWF0cml4OgoKYGBge3IgbWFrZV9zb2JqfQpzb2JqID0gU2V1cmF0OjpDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gc2NtYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvamVjdCA9ICJURDNBIikKc29iagpgYGAKCldlIGRvIG5vdCBuZWVkIHRoZSBjb3VudCBtYXRyaXg6CgpgYGB7ciBybV9tYXR9CnJtKHNjbWF0KQpgYGAKCgo8IS0tICMgR2xvYmFsIHNldHRpbmdzIC0tPgoKPCEtLSBXZSBkZWZpbmUgdGhlIG91dHB1dCBkaXJlY3RvcnkgdG8gc2F2ZSB0aGUgU2V1cmF0IG9iamVjdCBpbiB0aGlzIGVuZCBvZiB0aGlzIGZpbGU6IC0tPgoKPCEtLSBgYGB7ciBvdXRkaXIsIGV2YWw9RkFMU0V9IC0tPgo8IS0tIG91dGRpciA9ICIuL2RhdGEiIC0tPgoKPCEtLSBpZiAoIWRpci5leGlzdHMob3V0ZGlyKSkgeyAtLT4KPCEtLSAgIGRpci5jcmVhdGUob3V0ZGlyLCByZWN1cnNpdmUgPSBGQUxTRSkgLS0+CjwhLS0gfSAtLT4KPCEtLSBgYGAgLS0+CgpXZSBzZXQgdGhlIGZpbHRlcmluZyB0aHJlc2hvbGRzIGJhc2VkIG9uIHF1YWxpdHkgY29udHJvbC1yZWxhdGVkIG1ldHJpY3MuIEFkanVzdCB0aGVtIGFzIG5lY2Vzc2FyeSBiYXNlZCBvbiB0aGUgZmlndXJlcy4KCmBgYHtyIHNldF90aHJlc2hvbGRzfQpjdXRfbkNvdW50X1JOQSA9IDMwMApjdXRfbG9nMTBfbkNvdW50X1JOQSA8LSAzCmN1dF9uRmVhdHVyZV9STkEgPSA3NTAKY3V0X3BlcmNlbnRfbXQgPSA1CmN1dF9wZXJjZW50X3JiID0gMzAKY3V0X3BlcmNlbnRfc3QgPSA2CmBgYAoKV2UgZGVmaW5lIGEgbmljZSBwYWxldHRlIHRvIHZpc3VhbGl6ZSB0aGUgUUMgbWV0cmljczoKCmBgYHtyIGNvbG9yX3BhbGV0dGV9CmNvbG9yX3BhbGV0dGUgPSBjKCJsaWdodGdyYXkiLCAiI0ZEQkI4NCIsICIjRUY2NTQ4IiwgIiM3RjAwMDAiLCAiYmxhY2siKQpgYGAKCgpGb3IgdGhlIFFDIG1ldHJpY3MgcmVsYXRlZCB0byB0aGUgcHJvcG9ydGlvbiBvZiBVTUkgYmVsb25ncyB0byBhIHNwZWNpZmljIGdlbmUgc2V0cywgd2UgbmVlZCB0byBsb2FkIHRoZSBnZW5lIHNldHMuCgpgYGB7ciBsb2FkX2xpc3RzfQptaXRvX3N5bWJvbHMgPSByZWFkUkRTKG1pdG9fZmlsZSkKbWl0b19zeW1ib2xzCgpyaWJvX3N5bWJvbHMgPSByZWFkUkRTKHJpYm9fZmlsZSkKcmlib19zeW1ib2xzCgpzdHJlc3Nfc3ltYm9scyA9IHJlYWRSRFMoc3RyZXNzX2ZpbGUpCnN0cmVzc19zeW1ib2xzCmBgYAoKV2Uga2VlcCBvbmx5IHRoZSBnZW5lIHN5bWJvbHMgYXZhaWxhYmxlIGluIG91ciBkYXRhOgoKYGBge3Igc3Vic2V0X3N5bWJvbHN9Cm1pdG9fc3ltYm9scyA9IGludGVyc2VjdChtaXRvX3N5bWJvbHMsIHJvd25hbWVzKHNvYmopKQptaXRvX3N5bWJvbHMKCnJpYm9fc3ltYm9scyA9IGludGVyc2VjdChyaWJvX3N5bWJvbHMsIHJvd25hbWVzKHNvYmopKQpyaWJvX3N5bWJvbHMKCnN0cmVzc19zeW1ib2xzID0gaW50ZXJzZWN0KHN0cmVzc19zeW1ib2xzLCByb3duYW1lcyhzb2JqKSkKc3RyZXNzX3N5bWJvbHMKYGBgCgoqKk5vdGUqKjogQSBtb3JlIHJvYnVzdCBhbmFseXNpcyBtYXkgdXNlIHRoZSBnZW5lIGlkZW50aWZpZXJzIGluc3RlYWQgb2YgZ2VuZSBzeW1ib2xzLgoKIyBGYXN0LXByb2Nlc3NpbmcKClRvIHZpc3VhbGl6ZSB0aGUgbG93IHF1YWxpdHkgY2VsbHMsIHdlIGdlbmVyYXRlIGEgcHJvamVjdGlvbi4gVW5kZXJzdGFuZGluZyB0aGUgY29tbWFuZHMgYmVsb3cgaXMgdGhlIHB1cnBvc2Ugb2YgbmV4dCBjb3Vyc2Ugc2Vzc2lvbnMuIFNvIGp1c3QgcnVuOgoKYGBge3IgcXVpY2tfcHJvY2Vzc2luZywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc29iaiA9IFNldXJhdDo6Tm9ybWFsaXplRGF0YShzb2JqKQpzb2JqID0gU2V1cmF0OjpTY2FsZURhdGEoc29iaikKc29iaiA9IFNldXJhdDo6RmluZFZhcmlhYmxlRmVhdHVyZXMoc29iaikKc29iaiA9IFNldXJhdDo6UnVuUENBKHNvYmopCnNvYmogPSBTZXVyYXQ6OlJ1blVNQVAoc29iaiwgZGltcyA9IGMoMToyMCkpCgpzb2JqCmBgYAoKV2UgY2FuIG5vdyB2aXN1YWxpemUgdGhlIGNlbGxzIG9uIGEgMkQgcHJvamVjdGlvbjoKCmBgYHtyIHZpc3VhbGl6ZV9jZWxscywgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDh9ClNldXJhdDo6RGltUGxvdChzb2JqLAogICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgICAgY29scyA9ICJibGFjayIpCmBgYAoKIyBRdWFsaXR5IGNvbnRyb2wKCiMjIENvbXB1dGUgbWV0cmljcwoKV2hhdCBpcyBhbHJlYWR5IGF2YWlsYWJsZSBpbiB0aGUgU2V1cmF0IG9iamVjdCA/CgpgYGB7ciBzZWVfbWV0YWRhdGF9CmhlYWQoc29iakBtZXRhLmRhdGEpCmBgYAoKSG93IGRvIHRoZSB0d28gZmlyc3QgUUMgbWV0cmljcyB2YXJ5ID8KCmBgYHtyIHN1bW1hcnlfbWV0YWRhdGF9CnN1bW1hcnkoc29iakBtZXRhLmRhdGEpCmBgYAoKSW4gdGhlIGNvbHVtbiBgbkNvdW50X1JOQWAsIHRoZSBtYXhpbXVtIGlzIGZhciBmcm9tIHRoZSB0aGlyZCBxdWFydGlsZS4gRm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZSwgd2UgdHJhbnNmb3JtIHRoaXMgY29sdW1uIHRvIGxvZzEwIHNjYWxlLgoKYGBge3IgbG9nMTBfbmNvdW50X1JOQX0Kc29iaiRsb2cxMF9uQ291bnRfUk5BID0gbG9nMTAoc29iaiRuQ291bnRfUk5BKQoKc3VtbWFyeShzb2JqQG1ldGEuZGF0YSkKYGBgCgpXZSBjb21wdXRlIHRoZSBwZXJjZW50YWdlIG9mIFVNSSByZWxhdGVkIGZvciBlYWNoIG9mIHRoZSB0aHJlZSBsaXN0IG9mIGdlbmVzLiBGaXJzdCwgd2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIHRoZSBnZW5lcyBlbmNvZGVkIGluIHRoZSBtaXRvY2hvbmRyaWEsIHBlciBjZWxsLgoKYGBge3IgcGVyY2VudF9tdH0Kc29iaiA9IFNldXJhdDo6UGVyY2VudGFnZUZlYXR1cmVTZXQoCiAgc29iaiwKICBhc3NheSA9ICJSTkEiLAogIGZlYXR1cmVzID0gbWl0b19zeW1ib2xzLAogIGNvbC5uYW1lID0gInBlcmNlbnRfbXQiKQoKIyAjIEFsdGVybmF0aXZlIHdheSB0byBkbyAoYWxtb3N0KSB0aGUgc2FtZToKIyBzb2JqID0gU2V1cmF0OjpQZXJjZW50YWdlRmVhdHVyZVNldCgKIyAgIHNvYmosCiMgICBhc3NheSA9ICJSTkEiLAojICAgcGF0dGVybiA9ICJebXQtIiwKIyAgIGNvbC5uYW1lID0gInBlcmNlbnRfbXQiKQoKc3VtbWFyeShzb2JqJHBlcmNlbnRfbXQpCmBgYAoKVGhlbiwgd2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIHRoZSBnZW5lcyBlbmNvZGluZyByaWJvc29tYWwgdW5pdHMsIHBlciBjZWxsOgoKYGBge3IgcGVyY2VudF9yYn0Kc29iaiA9IFNldXJhdDo6UGVyY2VudGFnZUZlYXR1cmVTZXQoCiAgc29iaiwKICBhc3NheSA9ICJSTkEiLAogIGZlYXR1cmVzID0gcmlib19zeW1ib2xzLAogIGNvbC5uYW1lID0gInBlcmNlbnRfcmIiKQoKIyAjIEFsdGVybmF0aXZlIHdheSB0byBkbyAoYWxtb3N0KSB0aGUgc2FtZToKIyBzb2JqID0gU2V1cmF0OjpQZXJjZW50YWdlRmVhdHVyZVNldCgKIyAgIHNvYmosCiMgICBhc3NheSA9ICJSTkEiLAojICAgcGF0dGVybiA9ICJeUnBbbHxzXVthLXpdP1swLTldKlthLHhdPyQiLAojICAgY29sLm5hbWUgPSAicGVyY2VudF9yYiIpCgpzdW1tYXJ5KHNvYmokcGVyY2VudF9yYikKYGBgCgoKRmluYWxseSwgd2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIHN0cmVzcyBzaWduYXR1cmUsIHBlciBjZWxsOgoKYGBge3IgcGVyY2VudF9zdH0Kc29iaiA9IFNldXJhdDo6UGVyY2VudGFnZUZlYXR1cmVTZXQoCiAgc29iaiwKICBhc3NheSA9ICJSTkEiLAogIGZlYXR1cmVzID0gc3RyZXNzX3N5bWJvbHMsCiAgY29sLm5hbWUgPSAicGVyY2VudF9zdCIpCgpzdW1tYXJ5KHNvYmokcGVyY2VudF9zdCkKYGBgCgoKV2Uga25vdyBoYXZlIGFsbCB0aGUgUUMtcmVsYXRlZCBtZXRyaWNzOgoKYGBge3IgZmluYWxfc3VtbWFyeX0Kc3VtbWFyeShzb2JqQG1ldGEuZGF0YSkKYGBgCgojIyBGYWlsaW5nIGNlbGxzCgpXZSBpZGVudGlmeSB0aGUgY2VsbHMgdGhhdCBkbyBub3QgcGFzcyB0aGUgcXVhbGl0eSBjb250cm9sLiBUaGlzIHdpbGwgYmUgdXNlZCBmb3IgdGhlIHZpc3VhbGl6YXRpb24gYW5kIGZvciBmaWx0ZXJpbmcuIElmIHRoZSBmaWx0ZXJpbmcgdGhyZXNob2xkcyBhcmUgbW9kaWZpZWQsIGRvIG5vdCBmb3JnZXQgdG8gcnVuIGFnYWluIHRoaXMgY2h1bmsuCgpgYGB7ciBmYWlsX2NlbGxzfQpmYWlsX3BlcmNlbnRfbXQgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKHBlcmNlbnRfbXQgPiBjdXRfcGVyY2VudF9tdCkgJT4lCiAgcm93bmFtZXMoKQoKZmFpbF9wZXJjZW50X3JiID0gc29iakBtZXRhLmRhdGEgJT4lCiAgZHBseXI6OmZpbHRlcihwZXJjZW50X3JiID4gY3V0X3BlcmNlbnRfcmIpICU+JQogIHJvd25hbWVzKCkKCmZhaWxfcGVyY2VudF9zdCA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIocGVyY2VudF9zdCA+IGN1dF9wZXJjZW50X3N0KSAlPiUKICByb3duYW1lcygpCgpmYWlsX25Db3VudF9STkEgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKG5Db3VudF9STkEgPCBjdXRfbkNvdW50X1JOQSkgJT4lCiAgcm93bmFtZXMoKQoKZmFpbF9uRmVhdHVyZV9STkEgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKG5GZWF0dXJlX1JOQSA8IGN1dF9uRmVhdHVyZV9STkEpICU+JQogIHJvd25hbWVzKCkKYGBgCgojIyBWaXN1YWxpemF0aW9uCgpUaGlzIGlzIGRpZmZpY3VsdCB0byBoYW5kbGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGVzZSBtZXRyaWNzIGFjcm9zcyBjZWxscy4gV2Ugb3B0IGZvciB2YXJpb3VzIHZpc3VhbGl6YXRpb24gd2F5czoKCiogKipoaXN0b2dyYW0qKiwgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtZXRyaWMKKiAqKnZpb2xpbi9ib3ggcGxvdCoqLCBzaG93aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG1ldHJpYywgdXNlZnVsIGlmIHNldmVyYWwgZGF0YXNldHMgYXJlIGNvbnNpZGVyZWQKKiAqKlVNQVAqKiAob3IgYWx0ZXJuYXRpdmVseSwgdFNORSksIHNob3dpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbWV0cmljIG92ZXIgYSAyRCBwcm9qZWN0aW9uIG9mIGNlbGxzCgpZb3UgbWF5IGNob29zZSBvbmUgb2YgdGhlc2UgdmlzdWFsaXphdGlvbiB3YXlzLgoKIyMjIE51bWJlciBvZiBVTUkKCiMjIyMgRGVmaW5lIHRoZSBjb2RlCgpXZSB3cml0ZSBhIGxvdCBvZiBjb2RlIHRvIGNyZWF0ZSBvdXIgZmlndXJlcyBvZiBpbnRlcmVzdDoKCmBgYHtyIGRlZmluZV9jb2RlLCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDR9CiMgSGlzdG9ncmFtIHNob3dpbmc6CiMgLSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtZXRyaWMKIyAtIGFuIGVzdGltYXRlZCBkZW5zaXR5CiMgLSB0aGUgZmlsdGVyaW5nIHRocmVzaG9sZCBhcyBhIHN0cmFpZ2h0IGxpbmUKcF9oaXN0ID0gZ2dwbG90KHNvYmpAbWV0YS5kYXRhLCBhZXMoeCA9IGxvZzEwX25Db3VudF9STkEpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLCBmaWxsID0gIiNGODc2NkQiLCBiaW5zID0gMTAwKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMCwgY29sID0gImJsdWUiLCBsd2QgPSAwLjc1KSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gY3V0X2xvZzEwX25Db3VudF9STkEsIGNvbCA9ICJyZWQiKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiVGhyZXNob2xkIGZvciBsb2cxMF9uQ291bnRfUk5BIGlzOiAiLCBjdXRfbG9nMTBfbkNvdW50X1JOQSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBWaW9saW4gcGxvdDoKIyAtIGlzIGVhc2lseSBhY2Nlc3NpYmxlIGluIHRoZSBTZXVyYXQgcGFja2FnZQojIC0gY2FuIG9yIG5vdCBkaXNwbGF5IHRoZSBjZWxscyAoc2V0IGBwdC5zaXplID0gMGAgdG8gaGlkZSB0aGUgY2VsbHMpCiMgLSBpcyB1c2VmdWwgd2hlbiBzZXZlcmFsIGRhdGFzZXRzIGhhdmUgYmVlbiBtZXJnZWQKcF92aW9saW4gPSBTZXVyYXQ6OlZsblBsb3Qoc29iaiwgZmVhdHVyZXMgPSAibG9nMTBfbkNvdW50X1JOQSIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjdXRfbG9nMTBfbkNvdW50X1JOQSwgY29sID0gInJlZCIpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyAjIEJveCBwbG90IGlzIHNpbWlsYXIgdG8gdmlvbGluIHBsb3QgYnV0IG5vdCBpbiB0aGUgU2V1cmF0IHBhY2thZ2UKIyBwX2JveHBsb3QgPSBnZ3Bsb3Qoc29iakBtZXRhLmRhdGEsIGFlcyh5ID0gbG9nMTBfbkNvdW50X1JOQSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gb3JpZy5pZGVudCkpICsKIyAgIGdlb21fYm94cGxvdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsID0gIiNGODc2NkQiKSArCiMgICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMywgc2l6ZSA9IDAuMDAxKSArCiMgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjdXRfbG9nMTBfbkNvdW50X1JOQSwgY29sID0gInJlZCIpICsKIyAgIHRoZW1lX2NsYXNzaWMoKSArCiMgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpCgojIFRoaXMgMkQgcmVwcmVzZW50YXRpb24gc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbWV0cmljIG92ZXIgY2VsbHMKcF91bWFwID0gRmVhdHVyZVBsb3Qoc29iaiwKICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJsb2cxMF9uQ291bnRfUk5BIikgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjb2xvcl9wYWxldHRlKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKCiMgVGhpcyAyRCByZXByZXNlbnRhdGlvbiBzaG93cyB0aGUgbG93IHF1YWxpdHkgY2VsbHMgaW4gY29sb3IKIyBgb3JkZXIgPSAiZmFpbCJgIGlzIHVzZWQgdG8gZGlzcGxheSBmYWlsaW5nIGNlbGxzIG9uIGZyb250CiMgYmVmb3JlLCB3ZSBuZWVkIHRvIGRlZmluZSB0aGUgImZhaWxvcnBhc3MiIGNvbHVtbiBpbiBzb2JqQG1ldGEuZGF0YQojIEl0IGNvcnJlc3BvbmRzIHRvICJmYWlsIiBmb3IgY2VsbHMgdGhhdCBkbyBubyBwYXNzIHRoZSB0aHJlc2hvbGQsCiMgb3IgInBhc3MiIG90aGVyd2lzZQpzb2JqJGZhaWxvcnBhc3MgPSBpZmVsc2UoY29sbmFtZXMoc29iaikgJWluJSBmYWlsX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiZmFpbCIsIG5vID0gInBhc3MiKSAlPiUKICBhcy5mYWN0b3IoKQoKcF9mYWlsID0gRGltUGxvdChzb2JqLAogICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImZhaWxvcnBhc3MiLAogICAgICAgICAgICAgICAgIG9yZGVyID0gImZhaWwiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiNGODc2NkQiLCAiZ3JheTgwIiksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGxldmVscyhzb2JqJGZhaWxvcnBhc3MpKSArCiAgbGFicyh0aXRsZSA9ICJsb2cxMF9uQ291bnRfUk5BIiwKICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKGxlbmd0aChmYWlsX25Db3VudF9STkEpLCAiIGNlbGxzIGZhaWwgKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZCgxMDAqbGVuZ3RoKGZhaWxfbkNvdW50X1JOQSkvbmNvbChzb2JqKSwgMiksICIgJSkiKSkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgpzb2JqJGZhaWxvcnBhc3MgPSBOVUxMICMgcmVtb3ZlIHRoZSBjb2x1bW4gKGl0IHdhcyB0ZW1wb3JhcnkpCgojIFdlIHVzZSB0aGUgcGF0Y2h3b3JrIHBhY2thZ2UgdG8gYXJyYW5nZSBhbGwgZmlndXJlcyB0b2dldGhlcgpwYXRjaHdvcms6OndyYXBfcGxvdHMocF91bWFwLCBwX2ZhaWwsIHBfaGlzdCwgcF92aW9saW4pICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KG5yb3cgPSAxLCB3aWR0aHMgPSBjKDEsIDEsIDIsIDEpKQpgYGAKCiMjIyMgRGVmaW5lIGEgZnVuY3Rpb24KCkluc3RlYWQgb2YgY29weWluZy1wYXN0aW5nIHRoaXMgc2VjdGlvbiBvZiBjb2RlIGZvciBhbGwgUUMtbWV0cmljcywgd2UgZGVzaWduIGEgZnVuY3Rpb24gdXNpbmcgdGhlIHRlbXBsYXRlIGJlbG93OgoKYGBge3IgbXlfZnVuY3Rpb25fbmFtZSwgZXZhbCA9IEZBTFNFfQpteV9mdW5jdGlvbl9uYW1lID0gZnVuY3Rpb24ocGFyYW0xLCBwYXJhbTIpIHsKICAjIGRvIHNvbWV0aGluZyB3aXRoIHRoZSBwYXJhbWV0ZXIgdmFsdWVzCiAgb3V0cHV0ID0gInNvbWV0aGluZyIKICAKICByZXR1cm4ob3V0cHV0KQp9CmBgYAoKSGVyZSBpcyB0aGUgZnVuY3Rpb246CgpgYGB7ciBxY19wcmludF9mdW5jdGlvbn0KcHJpbnRfMV9xY19tZXRyaWMgPSBmdW5jdGlvbihvYmplY3QgPSBzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHFjID0gImxvZzEwX25Db3VudF9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dF9xYyA9IGN1dF9sb2cxMF9uQ291bnRfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhaWxpbmdfY2VsbHMgPSBmYWlsX25Db3VudF9STkEpIHsKICAjIERlc2NyaXB0aW9uIG9mIHRoZSBwYXJhbWV0ZXJzOgogICMgLSBzb2JqIDogdGhlIFNldXJhdCBvYmplY3QsIHdpdGggZGVmYXVsdCB2YWx1ZSB0byB0aGUgb25lCiAgIyAtIHFjIDogQ0hBUkFDVEVSIDogdGhlIFFDIG1ldHJpYywgbXVzdCBiZSBhIGNvbHVtbiBpbiBzb2JqQG1ldGEuZGF0YQogICMgLSBjdXRfcWMgOiBOVU1FUklDIDogdGhlIGZpbHRlcmluZyB0aHJlc2hvbGQgZm9yIHRoZSBRQyBtZXRyaWMKICAjIC0gZmFpbGluZ19jZWxscyA6IENIQVJBQ1RFUiBWRUNUT1IgOiB0aGUgY2VsbHMgdGhhdCBmYWlsIHRoZSBRQwogIAogICMgSGlzdG9ncmFtCiAgcF9oaXN0ID0gZ2dwbG90KG9iamVjdEBtZXRhLmRhdGEsIGFlcyh4ID0gLmRhdGFbW3FjXV0pKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IGFmdGVyX3N0YXQoZGVuc2l0eSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwgZmlsbCA9ICIjRjg3NjZEIiwgYmlucyA9IDEwMCkgKwogICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMCwgY29sID0gImJsdWUiLCBsd2QgPSAwLjc1KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjdXRfcWMsIGNvbCA9ICJyZWQiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKCJUaHJlc2hvbGQgZm9yICIsIHFjLCAiIGlzOiAiLCBjdXRfcWMpKSArCiAgICB0aGVtZV9jbGFzc2ljKCkgKwogICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCiAgCiAgIyBWaW9saW4gcGxvdAogIHBfdmlvbGluID0gU2V1cmF0OjpWbG5QbG90KG9iamVjdCwgZmVhdHVyZXMgPSBxYykgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0X3FjLCBjb2wgPSAicmVkIikgKwogICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogIAogICMgQm94IHBsb3QKICBwX2JveHBsb3QgPSBnZ3Bsb3Qob2JqZWN0QG1ldGEuZGF0YSwgYWVzKHkgPSAuZGF0YVtbcWNdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAib3JpZy5pZGVudCIpKSArCiAgICBnZW9tX2JveHBsb3QoY29sb3VyID0gImJsYWNrIiwgZmlsbCA9ICIjRjg3NjZEIikgKwogICAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjMsIHNpemUgPSAwLjAwMSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0X3FjLCBjb2wgPSAicmVkIikgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkKICAKICAjIEZlYXR1cmUgcGxvdAogIHBfdW1hcCA9IFNldXJhdDo6RmVhdHVyZVBsb3Qob2JqZWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBxYykgKwogICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbG9yX3BhbGV0dGUpICsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCiAgCiAgIyBEaW0gcGxvdAogIG9iamVjdCRmYWlsb3JwYXNzID0gaWZlbHNlKGNvbG5hbWVzKG9iamVjdCkgJWluJSBmYWlsaW5nX2NlbGxzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJmYWlsIiwgbm8gPSAicGFzcyIpICU+JQogICAgYXMuZmFjdG9yKCkKICAKICBwX2ZhaWwgPSBTZXVyYXQ6OkRpbVBsb3Qob2JqZWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJmYWlsb3JwYXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSAiZmFpbCIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRjg3NjZEIiwgImdyYXk4MCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGxldmVscyhvYmplY3QkZmFpbG9ycGFzcykpICsKICAgIGxhYnModGl0bGUgPSBxYywKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAobGVuZ3RoKGZhaWxpbmdfY2VsbHMpLCAiIGNlbGxzIGZhaWwgKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKDEwMCpsZW5ndGgoZmFpbGluZ19jZWxscykvbmNvbChzb2JqKSwgMiksICIgJSkiKSkgKwogICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCiAgCiAgIyBQYXRjaHdvcmsKICBwID0gcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBfdW1hcCwgcF9mYWlsLCBwX2hpc3QsIHBfdmlvbGluLCBwX2JveHBsb3QpICsKICAgIHBhdGNod29yazo6cGxvdF9sYXlvdXQobnJvdyA9IDEsIHdpZHRocyA9IGMoMSwgMSwgMiwgMSwgMSkpCiAgCiAgcmV0dXJuKHApCn0KYGBgCgojIyMjIENoZWNrIHRoZSBmdW5jdGlvbgoKVGhpcyBpcyB3b3JraW5nIGZvciBgbG9nMTBfbkNvdW50X1JOQWAsIGJlY2F1c2UgaXQgaXMgdXNlZCB0byBkZWZpbmUgZGVmYXVsdCBwYXJhbWV0ZXIgdmFsdWVzOgoKYGBge3IgY2hlY2sxX2xvZzEwX25Db3VudF9STkEsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gNH0KcHJpbnRfMV9xY19tZXRyaWMoKQpgYGAKCmJ1dCBhbHNvIHdvcmtzIGJ5IHNwZWNpZnlpbmcgdGhlIHBhcmFtZXRlciB2YWx1ZXM6CgpgYGB7ciBjaGVjazJfbG9nMTBfbkNvdW50X1JOQSwgZmlnLndpZHRoID0gMTgsIGZpZy5oZWlnaHQgPSA0fQpwcmludF8xX3FjX21ldHJpYyhzb2JqLAogICAgICAgICAgICAgICAgICBxYyA9ICJsb2cxMF9uQ291bnRfUk5BIiwKICAgICAgICAgICAgICAgICAgY3V0X3FjID0gY3V0X2xvZzEwX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgIGZhaWxpbmdfY2VsbHMgPSBmYWlsX25Db3VudF9STkEpCmBgYAoKU28gd2UgY2FuIGNvcHktcGFzdGUgb25seSB0aGlzIGNodW5rIGZvciB0aGUgbmV4dCBRQyBtZXRyaWNzICEKCiMjIyBOdW1iZXIgb2YgZ2VuZXMKCmBgYHtyIHNlZV9uRmVhdHVyZV9STkEsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gNH0KcHJpbnRfMV9xY19tZXRyaWMoc29iaiwKICAgICAgICAgICAgICAgICAgcWMgPSAibkZlYXR1cmVfUk5BIiwKICAgICAgICAgICAgICAgICAgY3V0X3FjID0gY3V0X25GZWF0dXJlX1JOQSwKICAgICAgICAgICAgICAgICAgZmFpbGluZ19jZWxscyA9IGZhaWxfbkZlYXR1cmVfUk5BKQpgYGAKCiMjIyBNaXRvY2hvbmRyaWFsIGdlbmVzIGV4cHJlc3Npb24KCmBgYHtyIHNlZV9wZXJjZW50X210LCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDR9CnByaW50XzFfcWNfbWV0cmljKHNvYmosCiAgICAgICAgICAgICAgICAgIHFjID0gInBlcmNlbnRfbXQiLAogICAgICAgICAgICAgICAgICBjdXRfcWMgPSBjdXRfcGVyY2VudF9tdCwKICAgICAgICAgICAgICAgICAgZmFpbGluZ19jZWxscyA9IGZhaWxfcGVyY2VudF9tdCkKYGBgCgojIyMgUmlib3NvbWFsIGdlbmVzIGV4cHJlc3Npb24KCmBgYHtyIHNlZV9wZXJjZW50X3JiLCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDR9CnByaW50XzFfcWNfbWV0cmljKHNvYmosCiAgICAgICAgICAgICAgICAgIHFjID0gInBlcmNlbnRfcmIiLAogICAgICAgICAgICAgICAgICBjdXRfcWMgPSBjdXRfcGVyY2VudF9yYiwKICAgICAgICAgICAgICAgICAgZmFpbGluZ19jZWxscyA9IGZhaWxfcGVyY2VudF9yYikKYGBgCgojIyMgU3RyZXNzLXJlbGF0ZWQgZ2VuZXMgZXhwcmVzc2lvbgoKYGBge3Igc2VlX3BlcmNlbnRfc3QsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gNH0KcHJpbnRfMV9xY19tZXRyaWMoc29iaiwKICAgICAgICAgICAgICAgICAgcWMgPSAicGVyY2VudF9zdCIsCiAgICAgICAgICAgICAgICAgIGN1dF9xYyA9IGN1dF9wZXJjZW50X3N0LAogICAgICAgICAgICAgICAgICBmYWlsaW5nX2NlbGxzID0gZmFpbF9wZXJjZW50X3N0KQpgYGAKCiMgRmlsdGVyaW5nCgpXZSBjb3VsZCBmaWx0ZXIgb3V0IGNlbGxzIGJhc2VkIG9uIHRoZXNlIDUgUUMgbWV0cmljcyBub3cuIEl0IGlzIGFsc28gcG9zc2libGUgdG8gd2FpdCwgcGVyZm9ybSB2YXJpb3VzIGFubm90YXRpb25zIHN1Y2ggYXMgY2VsbCB0eXBlIGFubm90YXRpb24gb3IgY2VsbCBjeWNsZSBwaGFzZSBzY29yaW5nLCB0byBiZXR0ZXIgY2hhcmFjdGVyaXplIHRoZSBsb3cgcXVhbGl0eSBjZWxscy4KClRvIGZpbHRlciBhIFNldXJhdCBvYmplY3QsIHdlIHVzZSB0aGUgYHN1YnNldGAgZnVuY3Rpb246CgpgYGB7ciBmaWx0ZXJfc29ian0Kc29ial9maWx0ZXJlZCA9IHN1YnNldChzb2JqLCBpbnZlcnQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgIGNlbGxzID0gdW5pcXVlKGMoZmFpbF9uQ291bnRfUk5BLCBmYWlsX25GZWF0dXJlX1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhaWxfcGVyY2VudF9tdCwgZmFpbF9wZXJjZW50X3JiLCBmYWlsX3BlcmNlbnRfc3QpKSkKc29ial9maWx0ZXJlZApgYGAKCldlIGFyZSBnb2luZyB0byBzYXZlIHRoZSBvYmplY3QgYW5ub3RhdGVkIGZvciBjZWxscyB0aGF0IGZhaWwgdGhlIHF1YWxpdHkgY29udHJvbCwgcmVnYXJkbGVzcyB0aGUgbWV0cmljcy4gU28gd2UgYWRkIGEgc2luZ2xlIGNvbHVtbiB0byBgc29iakBtZXRhLmRhdGFgIDoKCmBgYHtyIGFkZF9mYWlsX3FjfQpzb2JqJGZhaWxfcWMgPSBpZmVsc2UodGVzdCA9IGNvbG5hbWVzKHNvYmopICVpbiUgY29sbmFtZXMoc29ial9maWx0ZXJlZCksCiAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAicGFzcyIsCiAgICAgICAgICAgICAgICAgICAgICBubyA9ICJmYWlsIikKCnRhYmxlKHNvYmokZmFpbF9xYykKYGBgCgoKIyBTYXZlCgpXZSBzYXZlIHRoZSBub24tZmlsdGVyZWQgU2V1cmF0IG9iamVjdDoKCmBgYHtyIHNhdmVfc29iaiwgZXZhbD1GQUxTRX0Kc2F2ZVJEUyhzb2JqLCBmaWxlID0gcGFzdGUwKG91dHB1dF9kaXIsICIvc29ial9URDNBX3FjX2Fubm90YXRlZC5yZHMiKSkKYGBgCgpUaGlzIFNldXJhdCBvYmplY3QgY2FuIHRoZW4gYmUgdGhlIGlucHV0IG9iamVjdCBmb3IgYW5ub3RhdGlvbiwgZGVmaW5pdGlvbiBvZiBhIG5ldyBwcm9qZWN0aW9uIGFuZCBkb3duc3RyZWFtIGFuYWx5c2VzLgoKIyBSIHNlc3Npb24KClRoaXMgaXMgYSBnb29kIHByYWN0aWNlIHRvIHNob3cgdGhlIHZlcnNpb24gb2YgdGhlIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBub3RlYm9vay4KCmBgYHtyIHNlc3Npb25pbmZvLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==